package com.epam.training.concurrency.second.task2;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 
 * @author Bogdan
 *
 */
public class CircularBuffer {

	private Integer data[];

	private int head;
	private int tail;

	final Lock lock = new ReentrantLock();
	final Condition full = lock.newCondition();
	final Condition empty = lock.newCondition();

	/**
	 * Main constuctor
	 * 
	 * @param size
	 */
	CircularBuffer(int size) {
		data = new Integer[size];
		head = 0;
		tail = 0;
	}

	/**
	 * 
	 * @param value
	 */
	public void store(Integer value) {
		lock.lock();
		try {
			while (isFull()) {
				try {
					full.await();
				} catch (InterruptedException e) {
					Logger.getLogger(CircularBuffer.class.getName()).log(
							Level.SEVERE, null, e);
				}
			}

			data[tail++] = value;
			if (tail == data.length) {
				tail = 0;
			}

			empty.signal();
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 
	 * @return
	 */
	public Integer read() {
		lock.lock();
		try {
			while (isEmpty()) {
				try {
					empty.await();
				} catch (InterruptedException e) {
					Logger.getLogger(CircularBuffer.class.getName()).log(
							Level.SEVERE, null, e);
				}
			}

			int value = data[head++];
			if (head == data.length) {
				head = 0;
			}

			full.signal();
			return value;
		} finally {
			lock.unlock();
		}

	}

	/**
	 * 
	 * @return true if buffer full
	 */
	private boolean isFull() {
		return (tail + 1 == head) || (tail == (data.length - 1) && head == 0);
	}

	/**
	 * 
	 * @return true if buffer empty, false if buffer not empty
	 */
	public boolean isEmpty() {
		return head == tail;
	}

	/**
	 * 
	 * @return buffer size
	 */
	public int size() {
		return data.length;
	}
}
